package edu.hawaii.halealohacli;

import java.util.Scanner;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.util.tstamp.Tstamp;
import edu.hawaii.halealohacli.command.CurrentPower;
import edu.hawaii.halealohacli.command.DailyEnergy;
import edu.hawaii.halealohacli.command.EnergySince;
import edu.hawaii.halealohacli.command.MonitorGoal;
import edu.hawaii.halealohacli.command.MonitorPower;
import edu.hawaii.halealohacli.command.RankTowers;
import edu.hawaii.halealohacli.command.SetBaseline;
import edu.hawaii.halealohacli.processor.Processor;

/**
 * Provides interactivity for the user, ensures that the user enters a valid operation, and passes a
 * command index and the UI to the processor upon validation.
 * 
 * @author David Wilkie
 * @author Branden Ogata
 * @author Jayson T. Gamiao
 */
public class HaleAlohaClientUI {

  /** Becomes true when ready to quit. */
  private boolean finished = false;

  /**
   * Returns whether or not commands to this client have finished.
   * 
   * @return the finished
   */
  public boolean isFinished() {
    return this.finished;
  }

  /** The user's input from the console. */
  private Scanner scanner = new Scanner(System.in);

  /** Prompt to the user displayed in the console. */
  private static final String PROMPT = "> ";

  /**
   * Creates and starts a new StackUI application. Calls to the processor class and to the command
   * classes in the switch statements
   * 
   * @throws Exception upon error with the server.
   */
  public HaleAlohaClientUI() throws Exception {
    // initialize WattDepot Client
    // The WattDepot Client to query.
    WattDepotClient client = new WattDepotClient("http://server.wattdepot.org:8190/wattdepot/");

    // initialize commands
    // The Current Power Command.
    CurrentPower currentPower = new CurrentPower();
    /** The Daily Energy Command. */
    // The Daily Energy Command.
    DailyEnergy dailyEnergy = new DailyEnergy();
    // The Energy Since Command.
    EnergySince energySince = new EnergySince();
    // The Rank Towers Command.
    RankTowers rankTowers = new RankTowers();
    /** The Set Baseline Command. */
    SetBaseline setBaseline = new SetBaseline();
    /** The Monitor Power Command. */
    MonitorPower monitorPower;
    /** The Monitor Goal Command. */
    MonitorGoal monitorGoal = new MonitorGoal();

    Processor processor = new Processor(client);

    /**
     * This is the operation to be processed. It's trimmed by the prompt for operation method and
     * sent to the processor.
     */
    // default operation to 'help'
    // This is the operation to be processed. It's trimmed by the prompt for operation method
    // and sent to the processor.
    // default operation to 'help'
    Operation operation = Operation.help;
    // prompt the user for operation
    // Note: regarding coding standards, not certain how to handle static fields
    operation = promptForOperation(PROMPT);

    while (operation != Operation.quit) {
      try {
        switch (operation) {
        case currentPower:
          processor.process(operation.getString());
          currentPower.printResults(client, processor.getSource(), (XMLGregorianCalendar) null);
          break;
        case dailyEnergy:
          processor.process(operation.getString());
          dailyEnergy.printResults(client, processor.getSource(), processor.getTimestamp());
          break;
        case energySince:
          processor.process(operation.getString());
          energySince.printResults(client, processor.getSource(), processor.getTimestamp());
          break;
        case rankTowers:
          processor.process(operation.getString());
          rankTowers.printResults(client, null, processor.getBeginningTimestamp(),
              processor.getEndTimestamp());
          break;
        case setBaseline:
          processor.process(operation.getString());
          if (processor.getTimestamp() == null) {
            // Compute yesterday's time stamp.
            XMLGregorianCalendar yesterday = Tstamp.incrementDays(Tstamp.makeTimestamp(), -1);
            setBaseline.printResults(client, processor.getSource(), yesterday);
          }
          else {
            setBaseline.printResults(client, processor.getSource(), processor.getTimestamp());
          }

          // If the addition was successful, then add the newly created Baseline to monitorGoal
          if (setBaseline.getBaseline(processor.getSource().getName()) != null) {
            monitorGoal.addBaseline(setBaseline.getBaseline(processor.getSource().getName()));
          }
          break;
        case monitorPower:
          processor.process(operation.getString());
          monitorPower =
              new MonitorPower(processor.getSourceName(), client, processor.getInterval());
          monitorPower.printResults();
          // Getting the next line handles keystrokes from the loop for monitor-power.
          scanner.nextLine();
          break;
        case monitorGoal:
          processor.process(operation.getString());
          monitorGoal.setGoal(processor.getGoal());
          monitorGoal.setInterval(processor.getInterval());
          monitorGoal.printResults(client, processor.getSource());
          // Getting the next line handles keystrokes from the loop for monitor-goal
          this.scanner.nextLine();
          break;
        case help:
          help();
          break;
        default:
          break;
        }
        operation = promptForOperation(PROMPT);
      }
      catch (Exception e) {
        String error = e.getMessage();
        System.out.println("Caught an exception! Here's the message: \n" + error);
        if (error != null) {
          if (error.split(" ")[0].equals("400:")) {
            System.out.println("\nSorry, due to technical difficulties, "
                + "the data from WattDepot has been lost, the current range of data"
                + "is from 2011-11-24 to the current date. ");
            processor.clear();
          }
          if (error.split(" ")[0].equals("404:")) {
            System.out.println("\nSorry, there was an issue with the WattDepotServer, "
                + "please try again later");
          }
        }
        operation = promptForOperation(PROMPT);
      }
    }
  }

  /**
   * Asks user to enter a command.
   * 
   * @param prompt Displayed for the user at command line.
   * @return Operation The enumerated operation
   */
  private Operation promptForOperation(String prompt) {
    // prompt the user
    System.out.print(prompt);
    // read the user's entry from the console; strip surrounding whitespace
    String line = this.scanner.nextLine().trim();
    // determine operation based on the 1st character, assuming there is one
    if (line.length() != 0) {
      switch (line.charAt(0)) {
      case 'q':
        if ("quit".equals(line)) {
          return Operation.quit;
        }
        else {
          break;
        }
      case 'h':
        if ("help".equals(line)) {
          return Operation.help;
        }
        else {
          break;
        }
      case 'd':
        if (line.startsWith("daily-energy")) {
          Operation toReturn = Operation.dailyEnergy;
          toReturn.setString(line.substring(13));
          return toReturn;
        }
        else {
          break;
        }
      case 'e':
        if (line.startsWith("energy-since")) {
          Operation toReturn = Operation.energySince;
          toReturn.setString(line.substring(13));
          return toReturn;
        }
        else {
          break;
        }
      case 'c':
        if (line.startsWith("current-power")) {
          Operation toReturn = Operation.currentPower;
          toReturn.setString(line.substring(14));
          return toReturn;
        }
        else {
          break;
        }
      case 'r':
        if (line.startsWith("rank-towers")) {
          Operation toReturn = Operation.rankTowers;
          toReturn.setString(line.substring(12));
          return toReturn;
        }
        else {
          break;
        }
      case 's':
        if (line.startsWith("set-baseline")) {
          Operation toReturn = Operation.setBaseline;
          toReturn.setString(line.substring(13));
          return toReturn;
        }
        else {
          break;
        }
      case 'm':
        if (line.startsWith("monitor-power")) {
          try {
            Operation toReturn = Operation.monitorPower;
            toReturn.setString(line.substring(14));
            return toReturn;
          }
          catch (StringIndexOutOfBoundsException e) {
            System.out.println("monitor-power needs additional arguments\n");
            break;
          }
        }
        else if (line.startsWith("monitor-goal")) {
          Operation toReturn = Operation.monitorGoal;
          toReturn.setString(line.substring(13));
          return toReturn;
        }
        else {
          break;
        }
      default:
        break;
      // Intentionally left blank; all invalid input should go outside of the switch-case
      // statement to ensure consistent treatment
      }
    }
    System.out.print("Error! I couldn't understand your input.  ");
    return Operation.help;
  }

  /**
   * Prints instructions to the console.
   */
  private static void help() {
    System.out.println("Here are the available commands for this system.\n"
        + "current-power [tower | lounge]\n"
        + "    <Returns the current power in kW for the associated tower or lounge.>\n"
        + "daily-energy [tower | lounge] [date]\n"
        + "    <Returns the energy in kWh used by the tower or lounge for date specified "
        + "(yyyy-mm-dd).>\n" + "energy-since [tower | lounge] [date]\n"
        + "    <Returns the energy used since the date (yyyy-mm-dd) to now.>\n"
        + "rank-towers [start] [end]\n"
        + "    <Returns a list in sorted order from least to most energy consumed between the "
        + "[start] and [end] date (yyyy-mm-dd)>\n" + "set-baseline [tower | lounge] [date]\n"
        + "    <obtain and save the amount of energy used during each of the 24 hours of that "
        + "day for the " + "given tower or lounge.>\n"
        + "monitor-power [tower | lounge] [interval]\n"
        + "    <prints out a timestamp and the current power for [tower | lounge] every "
        + "[interval] seconds.>\n" + "monitor-goal [tower | lounge] goal interval\n"
        + "    <prints out a timestamp, the current power being consumed by the "
        + "[tower | lounge], and whether "
        + "or not the lounge is meeting its power conservation goal.>\n" + "quit\n"
        + "    <Terminates execution>\n\n"
        + "Note: towers are:  Mokihana, Ilima, Lehua, Lokelani\n"
        + "Lounges are the tower names followed by a \"-\" followed by one of A, B, C, D, E. "
        + "Forexample, Mokihana-A.\n");
  }
}